#include "../include/SbtNode.h"

namespace Sbt
{

Node::Node() : 
    mParentNode(0), 
    mNeedUpdateDerivedData(false), 
    mNeedUpdateAllChildren(false), 
    mDerivedRotation(0.0f),
    mDerivedPosition(Vector2::Zero),
    mDerivedScale(Vector2::Unit),
    mRelativeRotation(0.0f),
    mRelativePosition(Vector2::Zero),
    mRelativeScale(Vector2::Unit)
{

}

Node::Node( const String& name ) : 
    mName(name),
    mParentNode(0),
    mNeedUpdateDerivedData(false), 
    mNeedUpdateAllChildren(false),
    mDerivedRotation(0.0f),
    mDerivedPosition(Vector2::Zero),
    mDerivedScale(Vector2::Unit),
    mRelativeRotation(0.0f),
    mRelativePosition(Vector2::Zero),
    mRelativeScale(Vector2::Unit)
{

}

Node::Node( const String& name, Node* parent ) : 
    mName(name), 
    mParentNode(parent), 
    mNeedUpdateDerivedData(false), 
    mNeedUpdateAllChildren(false),
    mDerivedRotation(0.0f),
    mDerivedPosition(Vector2::Zero),
    mDerivedScale(Vector2::Unit),
    mRelativeRotation(0.0f),
    mRelativePosition(Vector2::Zero),
    mRelativeScale(Vector2::Unit)
{

}

Node::~Node()
{
    // clean up
}

void Node::addChildNode(Node* child, 
                        const Vector2& translate,
                        const float rotate /*= 0.0f*/ )
{
    if (child->mParentNode) {
        // report error
        return;
    }
    if (mChildNodeMap.find(child->mName) == mChildNodeMap.end()) {
        child->setRelativePosition(translate);
        child->setRelativeRotation(rotate);
        mChildNodeMap[child->mName] = child;
        child->setParentNode(this);
    }
}

void Node::setParentNode( Node* parent )
{
    mParentNode = parent;
}

Node* Node::getParentNode() const
{
    return mParentNode;
}

Node* Node::getNode( const String& name ) const
{
    ChildNodeMap::const_iterator result = mChildNodeMap.find(name);
    if (result != mChildNodeMap.end()) {
        return result->second;
    }
    return 0;
}

void Node::update()
{
    updateDerivedData();

    if (mNeedUpdateAllChildren)
    {
        updateAllChildren();
    }
    else
    {
        updateRequestedChildren();
    }
}

void Node::updateRequestedChildren()
{
    for (NeedUpdateChildrenSet::iterator iter = mChildrenNeedUpdate.begin();
        iter != mChildrenNeedUpdate.end(); ++iter)
    {
        (*iter)->update();
    }
}

void Node::updateAllChildren()
{
    mNeedUpdateAllChildren = false;
    for (ChildNodeMap::iterator iter = mChildNodeMap.begin();
        iter != mChildNodeMap.end(); ++iter)
    {
        iter->second->update();
    }
    mChildrenNeedUpdate.clear();
}

void Node::requestUpdate( Node* child )
{
    mChildrenNeedUpdate.insert(child);
    if (mParentNode) {
        mParentNode->requestUpdate(this);
    }
}

void Node::translate( const Vector2& translation, TransformSpace relative )
{
    switch (relative)
    {
    case RelativeToLocal:    
        mRelativePosition += Sbt::rotate(translation, mRelativeRotation);
        needUpdate();
        break;
    case RelativeToParent:
        mRelativePosition += translation;
        needUpdate();
        break;
    case RelativeToWorld:
        setDerivedPosition( getDerivedPosition() + translation );
        break;
    default:
        break;
    }
}

void Node::translate( const float x, const float y, TransformSpace relative )
{
    translate(Vector2(x, y), relative);
}

void Node::rotate( const float rotation, TransformSpace relative )
{
    switch (relative)
    {
    case RelativeToLocal:
    case RelativeToParent:
        mRelativeRotation += rotation;
        needUpdate();
        break;
    case RelativeToWorld:
        setDerivedRotation( getDerivedRotation() + rotation );
        break;
    }
}

void Node::scale( const Vector2& scale )
{
    mRelativeScale = scale;
    needUpdate();
}

void Node::scale( const float x, const float y )
{
    scale(Vector2(x, y));
}

void Node::setDerivedPosition( const Vector2& position )
{
    mDerivedPosition = position;
    if (mParentNode)
    {
        mRelativePosition = 
            Sbt::rotate( (mDerivedPosition - mParentNode->getDerivedPosition()), -mParentNode->getDerivedRotation() ) 
            / mParentNode->getDerivedScale();
    }
    else
    {
        mRelativePosition = mDerivedPosition;
    }
    needUpdate();
    mNeedUpdateDerivedData = false;
}

void Node::setDerivedRotation( const float rotation )
{
    mDerivedRotation = rotation;
    if (mParentNode)
    {
        mRelativeRotation = mDerivedRotation - mParentNode->getDerivedRotation();
    }
    else
    {
        mRelativeRotation = mDerivedRotation;
    }
    needUpdate();
    mNeedUpdateDerivedData = false;
}

void Node::setDerivedScale( const Vector2& scale )
{
    mDerivedScale = scale;
    if (mParentNode)
    {
        mRelativeScale = mDerivedScale / mParentNode->getDerivedScale();
    }
    needUpdate();
    mNeedUpdateDerivedData = false;
}

const Vector2& Node::getDerivedPosition() const
{
    if (mParentNode && mParentNode->mNeedUpdateAllChildren)
    {
        mParentNode->update();
    }
    if (mNeedUpdateDerivedData)
    {
        updateDerivedData();
    }
    return mDerivedPosition;
}

const Vector2& Node::getDerivedScale() const
{
    if (mParentNode && mParentNode->mNeedUpdateAllChildren)
    {
        mParentNode->update();
    }
    if (mNeedUpdateDerivedData)
    {
        updateDerivedData();
    }
    return mDerivedScale;
}

const float Node::getDerivedRotation() const
{
    if (mParentNode && mParentNode->mNeedUpdateAllChildren)
    {
        mParentNode->update();
    }
    if (mNeedUpdateDerivedData)
    {
        updateDerivedData();
    }
    return mDerivedRotation;
}

// protected
void Node::needUpdate()
{
    mNeedUpdateDerivedData = true;
    mNeedUpdateAllChildren = true;
    if (mParentNode)
    {
        mParentNode->requestUpdate(this);
    }
}

void Node::setUpdated() const
{
    mNeedUpdateDerivedData = false;
}

void Node::updateDerivedData() const
{
    if (mParentNode)
    {
        mDerivedRotation = mRelativeRotation + mParentNode->getDerivedRotation();
        mDerivedScale = mRelativeScale * mParentNode->getDerivedScale();
        mDerivedPosition = 
            Sbt::rotate(mParentNode->getDerivedScale() * mRelativePosition, mParentNode->getDerivedRotation()) 
            + mParentNode->getDerivedPosition();
    }
    else
    {
        mDerivedPosition = mRelativePosition;
        mDerivedRotation = mRelativeRotation;
        mDerivedScale = mRelativeScale;
    }
    setUpdated();
}

} // namespace Sbt